home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 2002-09-13 | 34.1 KB | 1,000 lines
//============================================================================== // Scn06p2: AI Scenario Script for scenario 6 Trojan player //============================================================================== /* AI owner: Dave Leary Scenario owner: Dave Leary AI for "Troy." Maintains hoplites, hippikons, and minotaurs at the start of the scenario, and adds peltasts and nemean lions after advancing to Age 3. Researches several techs as the scenario progresses. Sends two main attack group types - one with a primary unit of hoplites, and a raiding attack of hippikons which should prioritize villagers unless it runs into the player's army. A defend group of Colossi for the second dock is created once the player destroys the first dock, and ships are collected in a defend group in the harbor near the second dock. The hoplite groups randomly include the other maintained unit types. Attacks are enabled from the scenario once the player's first settlement has been claimed (the second scenario objective). The AI is cheating like mad. Don't look for it to gather resources; it has plenty of everything it might need. *** DIFFICULTY LEVEL NOTES *** Easy level - most towers removed. Two less colossi. Half as many units at the docks. Player starts with heavy infantry. Fewer units on patrol at game start. Cavalry raiding attacks never come. Moderate level - base difficulty level. HP starts with medium infantry and medium archers. First cavalry attack delayed an extra two minutes. Difficult level - enemy starts with boiling oil tech. More towers along main wall. Player does not have medium archers. Potential for Troy to do additional researches at the game progresses. Attack group sizes are larger. Colossi are slightly upgraded. Extra hypaspists at the docks. Nightmare - two towers at the second unoccupied settlement. One extra tower at each dock. Attack group sizes are larger. Colossus are very upgraded. Extra hypaspists at the docks. Walls around docks are upgraded to stone. */ // // Shared difficulty level variable. int difflevel=-1; //============================================================================== // Set Town Location //============================================================================== void setTownLocation(void) { //Look for the "Town Location" marker. kbSetTownLocation(kbGetBlockPosition("3989")); } //============================================================================== // miscStartup //============================================================================== void miscStartup(void) { // Get the difficulty level. difflevel=aiGetWorldDifficulty(); //Startup message(s). aiEcho(""); aiEcho(""); aiEcho("Scn06P2 AI Start, filename='"+cFilename+"'."); aiEcho("Difficulty Level="+difflevel+"."); //Spit out the map size. aiEcho(" Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+")."); //Cheat like a bastard. Once only, though. kbLookAtAllUnitsOnMap(); //Calculate some areas. kbAreaCalculate(1200.0); //Set our town location. setTownLocation(); //Reset random seeds aiRandSetSeed(); //Allocate all resources to the root escrow by setting percentage of military/economy to 0. kbEscrowSetPercentage( cEconomyEscrowID, cAllResources, 0.0 ); kbEscrowSetPercentage( cMilitaryEscrowID, cAllResources, 0.0 ); //Allocate all resources to the root escrow. kbEscrowAllocateCurrentResources(); // Drop the AI attack response distance for this player to 25 meters. aiSetAttackResponseDistance(25.0); } //============================================================================== // Attack stuff. //============================================================================== //Shared variables. int numberAttacks=0; int attackPlayerID=-1; bool makingAge3Units=false; //TODO: Decide how to rep attack group size. int attackMinimumGroupSize=4; int attackMaximumGroupSize=6; //Attack 1 vars. int attackPlan1ID=-1; int maintainPlan1ID=-1; //Attack 2 vars. int attackPlan2ID=-1; int maintainPlan2ID=-1; // Unit types int attackerUnitTypeID1=cUnitTypeHoplite; int attackerUnitTypeID2=cUnitTypeHippikon; int attackerUnitTypeID3=cUnitTypeToxotes; int attackerUnitTypeID4=cUnitTypePeltast; int attackerUnitTypeID5=cUnitTypeMinotaur; int attackerUnitTypeID6=cUnitTypeNemeanLion; int attackerUnitTypeID7=cUnitTypeShadeofHades; // Route and path vars int attackRoute1ID=-1; int attackPath1ID=-1; int attackRoute2ID=-1; int attackPath2ID=-1; //========================================================================================= // Kidd's cool configQuery function: used to create attack routes, etc. Oooh, lovin' that! //========================================================================================= bool configQuery( int queryID = -1, int unitType = -1, int action = -1, int state = -1, int player = -1, vector center = vector(-1,-1,-1), bool sort = false, float radius = -1 ) { if ( queryID == -1) { return(false); } if (player != -1) kbUnitQuerySetPlayerID(queryID, player); if (unitType != -1) kbUnitQuerySetUnitType(queryID, unitType); if (action != -1) kbUnitQuerySetActionType(queryID, action); if (state != -1) kbUnitQuerySetState(queryID, state); if (center != vector(-1,-1,-1)) { kbUnitQuerySetPosition(queryID, center); if (sort == true) kbUnitQuerySetAscendingSort(queryID, true); if (radius != -1) kbUnitQuerySetMaximumDistance(queryID, radius); } return(true); } //============================================================================== // Queryin' for enemies near the TC to the left. // This is used to drop sentinel on it if the player goes near it. //============================================================================== int checkForEnemies(void) { static int enemyQueryID=-1; vector leftTC=kbGetBlockPosition("4044"); int enemyCount=-1; if (enemyQueryID < 0) { // Doesn't exist, set it up enemyQueryID = kbUnitQueryCreate("Enemy Query"); // Get the number if ( configQuery( enemyQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 1, leftTC, false, 20 ) == false ) return(-1); } kbUnitQueryResetResults(enemyQueryID); enemyCount = kbUnitQueryExecute(enemyQueryID); return(enemyCount); } //============================================================================== // initAttack: Creates attack routes, etc. //============================================================================== void initAttack(int playerID=-1) { //Destroy all previous attacks (if this isn't the player we're already attacking. if (playerID != attackPlayerID) { //Reset the attack player ID. attackPlayerID=-1; //Destroy any previous attack plan. aiPlanDestroy(attackPlan1ID); attackPlan1ID=-1; aiPlanDestroy(attackPlan2ID); attackPlan2ID=-1; //Destroy our previous attack paths. kbPathDestroy(attackPath1ID); attackPath1ID=-1; kbPathDestroy(attackPath2ID); attackPath2ID=-1; //Destroy our previous attack routes. attackRoute1ID=-1; attackRoute2ID=-1; //Reset the number of attacks. numberAttacks=0; } //Save the player to attack. attackPlayerID=playerID; vector gatherPoint=kbGetBlockPosition("3989"); //Setup attack path 1 - go left attackPath1ID=kbPathCreate("Attack Path 1"); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3990")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3991")); kbPathAddWaypoint(attackPath1ID, kbGetBlockPosition("3992")); //Create attack route 1. attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPoint, kbGetBlockPosition("3995")); if (attackRoute1ID >= 0) kbAttackRouteAddPath(attackRoute1ID, attackPath1ID); //Setup attack path 2 - go right attackPath2ID=kbPathCreate("Attack Path 2"); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3990")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3991")); kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("3993")); //Create attack route 2. attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPoint, kbGetBlockPosition("3994")); if (attackRoute2ID >= 0) kbAttackRouteAddPath(attackRoute2ID, attackPath2ID); } //============================================================================== // setupHopliteAttack // // Hoplites head out, and sometimes bring their friends. // They go one of two routes. // They sometimes take Peltasts with 'em if it's Age 3. //============================================================================== bool setupHopliteAttack(int playerID=-1) { int randomPath=aiRandInt(2); int randomUnitTypes=aiRandInt(4); difflevel=aiGetWorldDifficulty(); //Info. aiEcho("Hoplite attack: attacking Player "+playerID+"."); //If the player to attack doesn't match, init the attack. if (attackPlayerID != playerID) { initAttack(playerID); if (attackPlayerID < 0) return(false); } //Create an attack plan. int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack); if (newAttackPlanID < 0) return(false); //Target player (required). This must work. if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false) return(false); //Gather point. vector gatherPoint=kbGetBlockPosition("3989"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false) return(false); //Unit types to attack. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit); //Attack route. if (randomPath == 0) { aiEcho("Attack going left."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID); } else { aiEcho("Attack going right."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID); } //Set the gather point and gather point distance. aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint); aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 40.0); //Set up the attack route usage pattern. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom); //Add the basic unit type to the plan. aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); //Add some extra guys if the random numbers come up right switch(randomUnitTypes) { // A couple of cavalry, if they're free. Take at least one, willya? case 0: { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 1, 2, 2); break; } // Throw in a single toxotes. There should be a bunch standing around somewhere. case 1: { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 0, 1, 1); break; } // A minotaur? Sure! We probably made one by now! But only if difflevel > easy. case 2: { if ( difflevel > 0 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 1, 2); } break; } // Oooh, ooh - or how about nothing? Those hoplites are fun enough by themselves! default: break; } // Shades - who doesn't love shades? If we're not sending much else, throw 'em in if you got 'em // (provided diff level is hard or better) if ( difflevel > 1 ) { if ( randomUnitTypes > 1 ) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID7, 0, 3, 3); } } // It's time for serious pain if peltasts are being made. // Two or three get tossed in the mix, plus a Nemean lion if one's around. Eat that! if (makingAge3Units) { aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 2, 3); aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID6, 0, 1, 1); } //Set the initial position. aiPlanSetInitialPosition(newAttackPlanID, gatherPoint); //Plan requires all need units to work (can be false). aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true); //Activate the plan. aiPlanSetActive(newAttackPlanID); //Now, save the attack plan ID appropriately. aiPlanSetOrphan(attackPlan1ID, true); attackPlan1ID=newAttackPlanID; //Increment our overall number of attacks. numberAttacks++; } //============================================================================== // setupRaidingAttack // // A couple of Hippikons head out to bash on villagers. //============================================================================== bool setupRaidingAttack(int playerID=-1) { difflevel=aiGetWorldDifficulty(); int randomPath=aiRandInt(2); //Info. aiEcho("Raiding attack: attacking Player "+playerID+"."); //If the player to attack doesn't match, init the attack. if (attackPlayerID != playerID) { initAttack(playerID); if (attackPlayerID < 0) return(false); } //Create an attack plan. int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack); if (newAttackPlanID < 0) return(false); //Target player (required). This must work. if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false) return(false); //Gather point. vector gatherPoint=kbGetBlockPosition("3989"); //Set the target type. This must work. if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false) return(false); //Unit types to attack - buildings...or, villagers on nightmare. if ( difflevel == 3 ) { aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeVillagerGreek); } else { aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeBuilding); } aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeUnit); //Attack route. if (randomPath == 0) { aiEcho("Attack going left."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID); } else { aiEcho("Attack going right."); aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID); } //Set the gather point and gather point distance. aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint); aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 40.0); //Set up the attack route usage pattern. aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom); //Add the basic unit type to the plan. aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize); //Set the initial position. aiPlanSetInitialPosition(newAttackPlanID, gatherPoint); //Plan requires all need units to work (can be false). aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true); //Cheat like a bastard when we go out raiding. 'Cause we can. :) kbLookAtAllUnitsOnMap(); //Activate the plan. aiPlanSetActive(newAttackPlanID); //Now, save the attack plan ID appropriately. aiPlanSetOrphan(attackPlan2ID, true); attackPlan2ID=newAttackPlanID; //Increment our overall number of attacks. numberAttacks++; } //============================================================================== // Droppin' some sentinel action. // If three enemies are close to the TC. //============================================================================== rule dropSentinel minInterval 15 active group ModerateAttackRules { int numEnemies=-1; vector leftTC=kbGetBlockPosition("4044"); numEnemies=checkForEnemies(); if (numEnemies > 3) { aiEcho("Casting Sentinel."); if ( aiCastGodPowerAtPosition(cTechSentinel, leftTC) == true ) xsDisableSelf(); else aiEcho("Sentinel failed - try again later."); } } //============================================================================== // Attack Generator 1 - Hoplites and friends //============================================================================== rule attackGenerator1 minInterval 300 inactive group AttackRules { //See how many "idle" attack plans we have. Don't create any more if we have //idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; //If we have enough unassigned military units, create a new attack plan, int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID1); aiEcho("There are "+numberAvailableUnits+" hoplites available for a new attack."); if (numberAvailableUnits >= attackMinimumGroupSize) setupHopliteAttack(1); } //============================================================================== // Attack Generator 2 - cavalry fun // They go after the villagers if they can //============================================================================== rule attackGenerator2 minInterval 360 inactive group AttackRules { //See how many "idle" attack plans we have. Don't create any more if we have //idle plans. int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack); if (numberIdleAttackPlans > 0) return; //If we have enough unassigned military units, create a new attack plan, int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID2); aiEcho("There are "+numberAvailableUnits+" hippikon available for a new raiding attack."); if (numberAvailableUnits >= attackMinimumGroupSize) setupRaidingAttack(1); } //===================================================================================== // 90 seconds after starting to research Age 3. // ...start maintaining Peltasts, Nemean Lions, and Siege Ships. //===================================================================================== rule age3MaintainSetup minInterval 90 inactive group AttackRules { int age3Active = kbGetTechStatus( cTechAge3Aphrodite ); int peltastsMaintained = 4; int lionsMaintained = 1; vector gatherPoint=kbGetBlockPosition("3989"); vector shipGatherPoint=kbGetBlockPosition("4446"); vector shipGatherPoint2=kbGetBlockPosition("4445"); // Verify we're actually where we need to be, then maintain away. if (age3Active == cTechStatusActive ) { int maintainPlan1ID=aiPlanCreate("Maintain "+peltastsMaintained+" "+kbGetProtoUnitName(attackerUnitTypeID4), cPlanTrain); if (maintainPlan1ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID4); //Makes total of fifteen, then stops. Player deserves a break. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToTrain, 0, 15); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, peltastsMaintained); //Don't train units faster than every 30 seconds aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 30); //Set a gather point. aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan1ID); } int maintainPlan2ID=aiPlanCreate("Maintain "+lionsMaintained+" "+kbGetProtoUnitName(attackerUnitTypeID6), cPlanTrain); if (maintainPlan2ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID6 ); //Makes total of six, then stops. Player deserves a break. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToTrain, 0, 6); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, lionsMaintained); //Don't train units faster than every 90 seconds aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 90); //Set a gather point. aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan2ID); } // Maintain plan for the siege ships, in case the player gets frisky. int shipMaintain1ID=aiPlanCreate("Maintain 3 "+kbGetProtoUnitName( cUnitTypeSiegeShipGreek ), cPlanTrain); if (shipMaintain1ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanUnitType, 0, cUnitTypeSiegeShipGreek); //Set the number of units to maintain in the world at one time. Only one if less than hard, else three. if ( difflevel >= 2 ) { aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanNumberToMaintain, 0, 3); } else { aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanNumberToMaintain, 0, 1); } // Slow training. aiPlanSetVariableInt(shipMaintain1ID, cTrainPlanFrequency, 0, 105); //Set a gather point. aiPlanSetVariableVector(shipMaintain1ID, cTrainPlanGatherPoint, 0, shipGatherPoint); //Activate the plan. aiPlanSetActive(shipMaintain1ID); } // Maintain plan for the ramming ships. int shipMaintain2ID=aiPlanCreate("Maintain 4 "+kbGetProtoUnitName( cUnitTypeRammingShipGreek ), cPlanTrain); if (shipMaintain2ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(shipMaintain2ID, cTrainPlanUnitType, 0, cUnitTypeRammingShipGreek); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(shipMaintain2ID, cTrainPlanNumberToMaintain, 0, 4); // Slow training. aiPlanSetVariableInt(shipMaintain2ID, cTrainPlanFrequency, 0, 105); //Set a gather point. aiPlanSetVariableVector(shipMaintain2ID, cTrainPlanGatherPoint, 0, shipGatherPoint2); //Activate the plan. aiPlanSetActive(shipMaintain2ID); } aiEcho("*** Now maintaining some Age 3 units ***"); // Set a shared boolean so that we can add Peltasts and Nemean Lions to the groups o' fun. makingAge3Units = true; //Done, disable. xsDisableSelf(); } } //============================================================================== // Tech Researching Rules - Age 2 //============================================================================== // Copper shields or copper mail? // AI picks one, randomly, at about the six-minute mark rule researchCopperSomething minInterval 360 active group ModerateResearches { int randomResearch=aiRandInt(2); int planID=aiPlanCreate("Doing some random copper thing research (five minutes).", cPlanResearch); if (planID < 0) return; if ( randomResearch == 1 ) { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperShields); } else aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechCopperMail); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy); aiPlanSetActive(planID); aiEcho("Researching some copper thing."); //Done. xsDisableSelf(); } // Medium Cavalry at about eight minutes. rule researchMediumCavalry minInterval 480 active group ModerateResearches { int planID=aiPlanCreate("Researching Medium Cavalry.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechMediumCavalry); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeStable); aiPlanSetActive(planID); aiEcho("Researching medium cavalry."); //Done. xsDisableSelf(); } // Try for Sarissa once, five minutes after starting on Age 3. // If you don't get it because of lack of favor, don't sweat it... // ...'cause you'll probably be getting a Nemean lion instead. rule researchSarissa minInterval 300 inactive group HardResearches { int planID=aiPlanCreate("Researching Sarissa.", cPlanResearch); if (planID < 0) return; aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechSarissa); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeArmory); aiPlanSetActive(planID); aiEcho("Researching Sarissa."); //Done. xsDisableSelf(); } // Heavy infantry or heavy archers? One of 'em, seven minutes after starting on Age 3. rule researchHeavySomething minInterval 420 inactive group HardResearches { int randomResearch=aiRandInt(2); int planID=aiPlanCreate("Doing some random heavy research (seven minutes).", cPlanResearch); if (planID < 0) return; if ( randomResearch == 1 ) { aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyInfantry); } else aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechHeavyArchers); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeAcademy); aiPlanSetActive(planID); aiEcho("Researching some heavy thing (either archers or infantry)."); //Done. xsDisableSelf(); } //==================================================================================== // Advance to Age 3 (Aphrodite) twelve minutes after the player builds his settlement. // Note he does not do this if the difficulty is Easy. //==================================================================================== rule researchAge3 minInterval 720 inactive group ModerateResearches { int planID=aiPlanCreate("Advancing to Age 3 Aphrodite.", cPlanResearch); if (planID < 0) return; aiEcho("*** ADVANCING TO AGE 3 ***"); aiPlanSetVariableInt(planID, cResearchPlanTechID, 0, cTechAge3Aphrodite); aiPlanSetVariableInt(planID, cResearchPlanBuildingTypeID, 0, cUnitTypeSettlementLevel1); aiPlanSetActive(planID); // Start makin' Age 3 stuff. xsEnableRule("age3MaintainSetup"); // Some Age 3 researching - only if enemy is "hard" or better. if ( difflevel >= 2 ) { xsEnableRule("researchSarissa"); xsEnableRule("researchHeavySomething"); } // Done. xsDisableSelf(); } // Delay on the initial cavalry attack, for moderate level. // This holds off on cavalry attacks for an extra two minutes on moderate only. rule fireCavalryRule minInterval 120 inactive { xsEnableRule("attackGenerator2"); xsDisableSelf(); } //============================================================================== // Tech Researching Timing Randomization - taken out for now. //============================================================================== /* void randomizeResearchTiming(void) { xsSetRuleMinInterval("researchCopperSomething", (aiRandInt(60) + 270)); xsSetRuleMinInterval("researchMediumCavalry", (aiRandInt(60) + 330)); } */ //===================================================================================== // Attack Enabler. This is called from the scenario with an AI Func effect // when the player claims his first settlement. //===================================================================================== void attackEnabler(int scriptCall = -1) { aiEcho("Settlement claimed, enemy attacks and other fun enabled."); xsEnableRule("attackGenerator1"); xsEnableRule("researchAge3"); // Enable cavalry attacks on all diff levels except easy. if ( difflevel > 1 ) { xsEnableRule("attackGenerator2"); } else if ( difflevel == 1 ) { xsEnableRule("fireCavalryRule"); } } //===================================================================================== // defendDock. This is called from the scenario when the player destroys the first // dock. It sets up a stiff defense at the other dock. //===================================================================================== void defendDock(int scriptCall = -1) { aiEcho("*** FIRST DOCK DESTROYED - COLOSSI/SIEGE SHIPS GOING TO DEFEND OTHER DOCK ***"); vector colossusDefense=kbGetBlockPosition("4346"); vector shipDefense=kbGetBlockPosition("4407"); vector playerTown=kbGetBlockPosition("4429"); int defendPlan1ID=aiPlanCreate("Colossus Defense", cPlanDefend); if (defendPlan1ID >= 0) { // Defend point if ( scriptCall == 0 ) { colossusDefense=kbGetBlockPosition("4346"); } else { colossusDefense=kbGetBlockPosition("4345"); } // Add the unit(s). aiPlanAddUnitType(defendPlan1ID, cUnitTypeColossus, 0, 3, 3); // Setup the vars. aiPlanSetDesiredPriority(defendPlan1ID, 50); aiPlanSetVariableVector(defendPlan1ID, cDefendPlanDefendPoint, 0, colossusDefense); aiPlanSetVariableFloat(defendPlan1ID, cDefendPlanEngageRange, 0, 45); // aiPlanSetUnitStance(defendPlan1ID, cUnitStanceDefensive); // aiPlanSetAllowUnderAttackResponse(defendPlan1ID, false); aiPlanSetActive(defendPlan1ID); } // On levels greater than easy, siege ships go attack the player's town (via a defense plan). if ( difflevel > 0 ) { int defendPlan2ID=aiPlanCreate("Siege Ship Defense 1", cPlanDefend); if (defendPlan2ID >= 0) { // Add the unit(s). aiPlanAddUnitType(defendPlan2ID, cUnitTypeSiegeShipGreek, 0, 3, 3); // Setup the vars. aiPlanSetDesiredPriority(defendPlan2ID, 40); aiPlanSetVariableVector(defendPlan2ID, cDefendPlanDefendPoint, 0, playerTown); aiPlanSetVariableFloat(defendPlan2ID, cDefendPlanEngageRange, 0, 20); aiPlanSetActive(defendPlan2ID); } } int defendPlan3ID=aiPlanCreate("Siege Ship Defense 2", cPlanDefend); if (defendPlan3ID >= 0) { // Defend point. if ( scriptCall == 0 ) { shipDefense=kbGetBlockPosition("4407"); } else { shipDefense=kbGetBlockPosition("4408"); } // Add the unit(s). aiPlanAddUnitType(defendPlan3ID, cUnitTypeSiegeShipGreek, 0, 2, 2); aiPlanAddUnitType(defendPlan3ID, cUnitTypeRammingShipGreek, 0, 4, 4); // Setup the vars. aiPlanSetDesiredPriority(defendPlan3ID, 60); aiPlanSetVariableVector(defendPlan3ID, cDefendPlanDefendPoint, 0, shipDefense); aiPlanSetVariableFloat(defendPlan3ID, cDefendPlanEngageRange, 0, 20); aiPlanSetActive(defendPlan3ID); } } //============================================================================== // MAIN. //============================================================================== void main(void) { //Startup. miscStartup(); //DAL cut - randomizeResearchTiming(); // Difficulty level adjustments - disable some researching and Sentinel usage. if ( difflevel == 0 ) { xsDisableRuleGroup("ModerateResearches"); xsDisableRuleGroup("ModerateAttackRules"); } if ( difflevel == 2 ) { attackMinimumGroupSize=6; attackMaximumGroupSize=8; } if ( difflevel == 3 ) { attackMinimumGroupSize=8; attackMaximumGroupSize=11; } //Share the number to maintain. int numberToMaintain=attackMinimumGroupSize*2; //Share a common gather point. vector gatherPoint=kbGetBlockPosition("3989"); vector gatherPoint2=kbGetBlockPosition("5593"); //Create a simple plan to maintain X hoplites. int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain); if (maintainPlan1ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1); //You can limit the number of units that are ever trained by this plan with this call. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 20 seconds aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 20); //Set a gather point. aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan1ID); } //Create a simple plan to maintain X hippikons int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain); if (maintainPlan2ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2); //You can limit the number of units that are ever trained by this plan with this call. //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain); //Don't train units faster than every 45 seconds aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 45); //Set a gather point. aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPoint2); //Activate the plan. aiPlanSetActive(maintainPlan2ID); } //Create a simple plan to maintain a couple of minotaurs. int maintainPlan3ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID5), cPlanTrain); if (maintainPlan3ID >= 0) { //Must set the type of unit to train. aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID5); //Set the number of units to maintain in the world at one time. aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, 3); //These things train slowly. Like, every two minutes at best. //That said, favor will probably be the limiter. He's got two villagers makin' favor //and I don't give him any extra. aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 120); //Set a gather point. aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPoint); //Activate the plan. aiPlanSetActive(maintainPlan3ID); } }